/*
 *
 *  * Copyright:  2018 smarabbit studio.
 *  *
 *  *  Licensed under the Confluent Community License; you may not use this file
 *  *  except in compliance with the License.  You may obtain a copy of the License at
 *  *
 *  *  http://www.confluent.io/confluent-community-license
 *  *
 *  *  Unless required by applicable law or agreed to in writing, software
 *  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  *  specific language governing permissions and limitations under the License.
 *  *
 *  *  @作   者： 黄开晖 (117227773@qq.com)
 *  *  @日   期:    ${DATE}
 *
 */

package com.massyframework.beanskin.launching.web;

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContext;
import com.massyframework.beanskin.framework.Framework;

/**
 * {@link FrameworkStartStopListener}, 运行框架启停监听器
 */
public final class FrameworkStartStopListener extends ServletContextListenerDispatcher {

	private static boolean STARTED;

	public FrameworkStartStopListener() {
		this(false);
	}

	public FrameworkStartStopListener(boolean started) {
		STARTED = started;
	}

	@Override
	public synchronized void contextInitialized(ServletContextEvent sce) {
		if (!STARTED) {
			Framework framework = this.retrieveFramework(sce.getServletContext());
			if (framework != null) {
				this.setAssemblyContext(framework.findModuleById(0, Assembly.class).getAssemblyContext());
				try {
					framework.start();
				} catch (Exception e) {
					sce.getServletContext().log("start framwork failed.", e);
				}
			} else {
				sce.getServletContext().log("cannot found Framework from ServletContext.");
			}
			
			super.contextInitialized(sce);
			
			STARTED = true;
		}
	}

	@Override
	public synchronized void contextDestroyed(ServletContextEvent sce) {
		if (!STARTED) {
			Framework framework = this.retrieveFramework(sce.getServletContext());
			if (framework != null) {
				this.clearClassFileTransformer(framework);
				try {
					framework.stop();
				} catch (Exception e) {
					sce.getServletContext().log("stop framwork failed.", e);
				}
				
				super.contextDestroyed(sce);
	
				try {
					this.setAssemblyContext(null);
					framework.destory();
					sce.getServletContext().removeAttribute(Framework.class.getName());
					URLClassLoader classLoader = (URLClassLoader) framework.getClass().getClassLoader();
					classLoader.close();
					System.gc();
				} catch (Exception e) {
					sce.getServletContext().log("destory framwork failed.", e);
				}
				
				
	
			} else {
				sce.getServletContext().log("cannot found Framework from ServletContext.");
			}

			STARTED = false;
		}
	}

	/**
	 * 清理注册的类文件转换器
	 * 
	 * @param framework {@link Framework}
	 */
	protected void clearClassFileTransformer(Framework framework) {
		Assembly kernel = framework.findModuleById(0, Assembly.class);
		AssemblyContext context = kernel.getAssemblyContext();
		if (context.containsBean(Instrumentation.class.getName())) {
			Instrumentation inst = context.getBean(Instrumentation.class.getName(), Instrumentation.class);
			try {
				Method method = inst.getClass().getDeclaredMethod("clear", new Class<?>[0]);
				method.invoke(inst, new Object[0]);
			} catch (Exception e) {

			}

		}

	}

	protected Framework retrieveFramework(ServletContext servletContext) {
		Object result = servletContext.getAttribute(Framework.class.getName());
		if (result != null) {
			if (Framework.class.isAssignableFrom(result.getClass())) {
				return Framework.class.cast(result);
			}
		}
		return null;
	}
}
